source("MakePhyloseqObject.R")
library(phyloseq)
library(DESeq2)
library(dplyr)
library(ggplot2)
library(vegan)
library(ggrepel)
library(cowplot)
 library(lme4)
library(lmerTest)
 library(broom.mixed)
library(plotly)

Attaching package: ‘plotly’

The following object is masked from ‘package:IRanges’:

    slice

The following object is masked from ‘package:S4Vectors’:

    rename

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout

Better names for taxa

library(tidyverse)
pull_lowlev <- function(taxaVec){
  taxaVec <- dplyr::select(Kingdom:Genus)
  taxaVec <- na.omit(taxaVec)
  taxaVec[length(taxaVec)]
}

theTaxa <- ps %>% tax_table() %>% as("matrix") %>% data.frame()  #%>% as.data.frame()
theTaxa[] <- lapply(theTaxa, as.character)
theTaxa$ASVNum <- rownames(theTaxa) %>% parse_number
theTaxa$ASVName <- rownames(theTaxa)


myLowist <- theTaxa %>% pivot_longer(Kingdom:Genus) %>% na.omit %>% group_by(ASVName) %>% summarise(Lowist = last(value))

theTaxa <- theTaxa %>% left_join(myLowist, by = "ASVName")
theTaxa <- theTaxa %>% mutate(JName = str_c(Lowist, ASVNum, sep = "_"))
theTaxa <- theTaxa %>% column_to_rownames("ASVName")
head(theTaxa)


tt2 <- tax_table(theTaxa)
Coercing from data.frame class to character matrix 
prior to building taxonomyTable. 
This could introduce artifacts. 
Check your taxonomyTable, or coerce to matrix manually.
rownames(tt2) <- rownames(theTaxa)
colnames(tt2) <- colnames(theTaxa)
ps_retax <- ps
tax_table(ps_retax) <- tt2

Remove blanks

ps_noblank <- subset_samples(ps_retax, Strain != "Blank")
ps_plusone <- transform_sample_counts(ps_noblank, function(x) x + 1)

Convert to relative abundance

psra <- ps %>% transform_sample_counts( function(x) x/sum(x))

Normalize microibal counts to oyster counts

ps_oyster <- ps %>% subset_taxa(Order == "Ostreoida")
ps_not_oyster <- ps %>% subset_taxa(Order != "Ostreoida")
oyster_sums <- otu_table(ps_oyster)@.Data %>% apply(MARGIN = 2, sum)
not_oyster_counts <- otu_table(ps_not_oyster)@.Data

over_oyster <- sweep(not_oyster_counts, 2, oyster_sums, "/")
over_oyster_log10 <- log10(over_oyster)

This is a variance normalizing tranformaiton. Its apparently an alternative to rarifying the data.

deseq_pre <- phyloseq_to_deseq2(ps, design = ~ Project)
# deseq_counts <- estimateSizeFactors(deseq_pre, type = "poscounts")
# deseq_counts_vst <- varianceStabilizingTransformation(deseq_counts)
# vst_trans_count_tab <- assay(deseq_counts_vst)

# Ella on Slack
#deseq_pre
dds = deseq_pre[rowSums(counts(deseq_pre)) > 5,]
dds_esf <- estimateSizeFactors(dds, type = "poscounts")
dds01 <- DESeq(dds_esf)
dds_res <- results(dds01)

dds_counts <- counts(dds01, normalized = TRUE)

ps_dds <- ps_retax
otu_table(ps_dds) <- otu_table(dds_counts, taxa_are_rows = TRUE)

ets filter just the rows

ps_common <- filter_taxa(ps_dds, function(x) sum(x > 2) > (0.2*length(x)), TRUE)
ps_common
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 463 taxa and 35 samples ]
sample_data() Sample Data:       [ 35 samples by 6 sample variables ]
tax_table()   Taxonomy Table:    [ 463 taxa by 9 taxonomic ranks ]

Interestingly, this step doesn’t seem to effect the ordinations much.

ps_oyster <- ps_dds %>% subset_taxa(Order == "Ostreoida")
ps_not_oyster <- ps_common %>% subset_taxa(Order != "Ostreoida") # ok if not common, so just using dds
oyster_sums <- otu_table(ps_oyster)@.Data %>% apply(MARGIN = 2, sum)
not_oyster_counts <- otu_table(ps_not_oyster)@.Data

over_oyster <- sweep(not_oyster_counts, 2, oyster_sums, "/")
# so this suddently contains zeros, making everything fail, but it didn't used to. What gives?
over_oyster_log10 <- log10(over_oyster)
(detection_thresh <- min(na.omit(over_oyster[over_oyster > 0])))
[1] 7.091444e-06
over_oyster_log10 <- log10(over_oyster + detection_thresh)

ps_oo <- ps_not_oyster
otu_table(ps_oo) <- otu_table(over_oyster, taxa_are_rows = TRUE)
ps_oo <- subset_samples(ps_oo, (SampleID %in% c(names(oyster_sums[oyster_sums > 0])))) # remove cases

ps_oo_log <- ps_not_oyster
otu_table(ps_oo_log) <- otu_table(over_oyster_log10, taxa_are_rows = TRUE)

ps_oo_log_ss <- subset_samples(ps_oo_log, !(Project =="Mock"& Strain == "Even" & Run == "NoCrash"))
ps_oo_ss <- subset_samples(ps_oo, !(Project =="Mock"& Strain == "Even" & Run == "NoCrash"))

Make the one figure for the paper

A function I use

converts phyloseq sample data to a data frame

samd_to_df <- function(samd){
  df <- samd %>% sample_data %>% .@.Data %>% lapply(as.character) %>% data.frame
  colnames(df) <- samd@names
  rownames(df) <- samd@row.names
  df
}

Actually run the RDA


ps_oo_log_ss1 <- ps_oo_log_ss %>% subset_samples(((Run =="NoCrash" & Project == "NoCrash") | (Run == "Crash4" & Project == "Crash")) & Treatment %in% c("Fed", "Starve", "Pre"))

test_rda <- rda(t(otu_table(ps_oo_log_ss1)) ~ Project + as.factor(Strain == "Wild") + as.factor(Treatment == "Fed"), data = samd_to_df(sample_data(ps_oo_log_ss1)))
test_rda
Call: rda(formula = t(otu_table(ps_oo_log_ss1)) ~ Project + as.factor(Strain == "Wild") + as.factor(Treatment == "Fed"),
data = samd_to_df(sample_data(ps_oo_log_ss1)))

               Inertia Proportion Rank
Total         398.5716     1.0000     
Constrained   219.1114     0.5497    3
Unconstrained 179.4602     0.4503   24
Inertia is variance 

Eigenvalues for constrained axes:
  RDA1   RDA2   RDA3 
168.88  41.18   9.06 

Eigenvalues for unconstrained axes:
  PC1   PC2   PC3   PC4   PC5   PC6   PC7   PC8 
45.32 28.06 17.27 13.72  8.04  6.94  5.75  5.37 
(Showing 8 of 24 unconstrained eigenvalues)
test_rda_anova <- anova(test_rda, by = "margin", permutations = how(nperm = 99999))
test_rda_anova
Permutation test for rda under reduced model
Marginal effects of terms
Permutation: free
Number of permutations: 99999

Model: rda(formula = t(otu_table(ps_oo_log_ss1)) ~ Project + as.factor(Strain == "Wild") + as.factor(Treatment == "Fed"), data = samd_to_df(sample_data(ps_oo_log_ss1)))
                              Df Variance       F  Pr(>F)    
Project                        1  154.338 20.6403   1e-05 ***
as.factor(Strain == "Wild")    1   18.132  2.4248 0.05165 .  
as.factor(Treatment == "Fed")  1   38.107  5.0962 0.00232 ** 
Residual                      24  179.460                    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
myScores <- scores(test_rda, choices = c(1:4), scaling = "symmetric")

Sample Data

mySamples <- left_join(
myScores$sites %>% data.frame %>% rownames_to_column("Sample"),
ps_oo_log_ss1 %>% sample_data() %>% samd_to_df %>% rownames_to_column("Sample"),
by = "Sample"
)

Percent variance explained

eigsum <- sum(c(test_rda$CCA$eig, test_rda$CA$eig))
cca_eig <- test_rda$CCA$eig / eigsum
ca_eig <- test_rda$CA$eig / eigsum
all_eig <- c(cca_eig, ca_eig)
all_eig["RDA1"] 
     RDA1 
0.4237047 
rda_eig_pct <- data.frame(all_eig) %>% rownames_to_column("Ax") %>% dplyr::rename(EigPct = "all_eig") %>%
  mutate(Axis2 = ordered(Ax, levels = Ax))
rda_eig_pct

Species

Select which species we will show, targeting ones that are far from 0, 0.

mySpecies <- left_join(
  myScores$species %>% data.frame %>% rownames_to_column("ASV"),
ps_oo_log_ss1 %>% tax_table() %>% .@.Data %>% as.data.frame %>% rownames_to_column("ASV")
) %>% mutate(RDADist = sqrt(RDA1^2 + RDA2^2 + RDA3 ^2)) %>% 
  arrange(-RDADist) %>%
  head(10) %>%
  mutate(Rank = 1:10)
Joining, by = "ASV"
mySpecies

mySpecies %>% ggplot(aes(x = RDA1, y = RDA2, label = JName)) + geom_point() + ggrepel::geom_text_repel()

Calculate Centroids

myCent <- myScores$centroids %>% data.frame %>% rownames_to_column("Treatment") %>%
  tidyr::extract(Treatment, c("Type", "Condition"), "([A-Z][a-z]+)([A-Z].*)",  remove = FALSE) %>%
  mutate(Type = if_else(str_detect(Treatment, "Wild"), "Strain", Type)) %>%
  mutate(Condition = if_else(Type == "Strain" & str_detect(Treatment, "FALSE"), "Tame", Condition)) %>%
  mutate(Condition = if_else(Type == "Strain" & str_detect(Treatment, "TRUE"), "Wild", Condition)) %>%
  mutate(Type = if_else(str_detect(Treatment, "Fed"), "Feeding", Type)) %>%
  mutate(Condition = if_else(Type == "Feeding" & str_detect(Treatment, "FALSE"), "StarvedOrPre", Condition)) %>%
  mutate(Condition = if_else(Type == "Feeding" & str_detect(Treatment, "TRUE"), "Fed", Condition))

myCent2 <- myCent %>% filter((Condition %in% c("Fed", "Wild", "Crash")))

Main Figure

ccaPlot_1V2_A <- mySamples %>% ggplot(aes(x = RDA1, y = RDA2)) +
  geom_point(size = 3, stroke = 3, aes(shape = Project, color = Strain == "Wild", fill = Treatment), alpha = 1) +
  scale_shape_manual(values = c(21:25)) + scale_color_manual(values = c("gray40", "black")) + scale_fill_manual(values = c(Fed = "Blue", Pre = "DarkGreen", Starve = "Orange", Crash = "Pink", NoCrash = "White", Wild = "White"))+
  geom_point(data = mySpecies, size = 4, shape = "+") + 
  ggrepel::geom_text_repel(data = mySpecies, aes(label = JName) , size = 3) +
  guides(fill = guide_legend(override.aes = list(shape = 21)), color = guide_legend(override.aes = list(shape = 21))) +
  theme(legend.position = "bottom")

ccaLegend <- get_legend(ccaPlot_1V2_A)

ccaPlot_1V2_B <- ccaPlot_1V2_A +
  geom_label(data = myCent2, aes(label = Condition, x = RDA1 * 1.75, y = RDA2 * 2, fill = Condition) , size = 5) +
  geom_segment(data = myCent2, aes(x = 0, y = 0, xend = RDA1 * 2.5, yend = RDA2 * 2.5), arrow = arrow(length = unit(0.1, "in")), alpha = 0.5, size = 1) +
  coord_fixed(sqrt(test_rda$CCA$eig[2]/test_rda$CCA$eig[1])) +
  labs(x = paste0("RDA1", " (", scales::percent(all_eig["RDA1"]), ")"),
       y = paste0("RDA2", " (", scales::percent(all_eig["RDA2"]), ")")
       ) +
  cowplot::theme_cowplot() + theme(legend.position = "none")

#plot_grid(ccaPlot_1V2_B, ccaLegend)
ProtoNewFig <- plot_grid(ccaPlot_1V2_B, ccaLegend, nrow = 2, rel_heights = c(10,1))
ggsave("ProtoNewFig.svg", ProtoNewFig, width = 8, height = 6)

View Main Figure

ProtoNewFig

I’m not sure why “NoCrash” and “Wild” showed up in the legend. It didn’t used to do that, but I’m not going to bother to correct this right now.

Seeing which species relate to crash vs non-crash

We don’t show these figures in the paper, but we do refer to them.

Initial data wrangling

ps_oo_log_ss2 <- ps_oo_log_ss1
sample_data(ps_oo_log_ss2) <-  mySamples %>% column_to_rownames("Sample") %>% sample_data()

Reshaping to long takes a little while. (~ 20 seconds)

melt_oo_log_ss2 <- psmelt(ps_oo_log_ss2)
melt2_oo_log_ss2 <- melt_oo_log_ss2 %>%
  mutate(logAbundance = Abundance) %>%
  mutate(Abundance = 10^(Abundance))
melt2_oo_log_ss2 <- melt2_oo_log_ss2 %>% left_join(mySpecies %>% select(RDA1.Spec = RDA1, JName), by = "JName")
melt2_oo_log_ss2 %>% head

Stuff

Run an lme to see if each microbe is related to project, holding out treatment as a mixed effect.

modframe <- melt2_oo_log_ss2 %>% select(Project:Group, logAbundance, Kingdom:Genus, JName ) %>% group_by(JName) %>% nest(data = Project:logAbundance) %>%
  #mutate(mod = map(data, ~tidy(lm(data = ., logAbundance ~ Project)))) %>%
  mutate(lme = map(data, ~tidy(lmer(data = ., logAbundance ~ Project + (1|Treatment) + (1|Strain)))))
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
modframe01 <- modframe %>% unnest(lme) %>% filter(term == "ProjectNoCrash") %>% select(Kingdom:JName, estimate, std.error, p.value) %>% mutate(fdr = p.adjust(p.value, method = "BH"))
ggplotly(
ggplot(modframe01, aes(x = estimate, y = log10(p.value),  color = Kingdom, JName = JName)) + geom_point() + 
  scale_color_manual(values = c(Bacteria = "Gray10", Eukaryota = "blue", Archaea = "red")) +
  geom_hline(aes(yintercept = log10(0.01)))
)

Everything below the line is statistically significant. Mouse over the dots to see which bacteria are which. If plotly is giving you problems, comment out the ggplotly bits and this shows up as a normal plot.

How many significant and non significant ASVs are there?

modframe01 %>% ungroup %>% summarise(signif = sum(p.value < 0.01), total = length(p.value)) %>% mutate(frachits = signif/total) 

63 % of the asvs are related to treatment p < 0.01.

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CnNvdXJjZSgiTWFrZVBoeWxvc2VxT2JqZWN0LlIiKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KHBoeWxvc2VxKQpsaWJyYXJ5KERFU2VxMikKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHZlZ2FuKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkoY293cGxvdCkKIGxpYnJhcnkobG1lNCkKbGlicmFyeShsbWVyVGVzdCkKIGxpYnJhcnkoYnJvb20ubWl4ZWQpCmxpYnJhcnkocGxvdGx5KQoKYGBgCgojIEJldHRlciBuYW1lcyBmb3IgdGF4YQoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCgpgYGB7cn0KcHVsbF9sb3dsZXYgPC0gZnVuY3Rpb24odGF4YVZlYyl7CiAgdGF4YVZlYyA8LSBkcGx5cjo6c2VsZWN0KEtpbmdkb206R2VudXMpCiAgdGF4YVZlYyA8LSBuYS5vbWl0KHRheGFWZWMpCiAgdGF4YVZlY1tsZW5ndGgodGF4YVZlYyldCn0KCnRoZVRheGEgPC0gcHMgJT4lIHRheF90YWJsZSgpICU+JSBhcygibWF0cml4IikgJT4lIGRhdGEuZnJhbWUoKSAgIyU+JSBhcy5kYXRhLmZyYW1lKCkKdGhlVGF4YVtdIDwtIGxhcHBseSh0aGVUYXhhLCBhcy5jaGFyYWN0ZXIpCnRoZVRheGEkQVNWTnVtIDwtIHJvd25hbWVzKHRoZVRheGEpICU+JSBwYXJzZV9udW1iZXIKdGhlVGF4YSRBU1ZOYW1lIDwtIHJvd25hbWVzKHRoZVRheGEpCgoKbXlMb3dpc3QgPC0gdGhlVGF4YSAlPiUgcGl2b3RfbG9uZ2VyKEtpbmdkb206R2VudXMpICU+JSBuYS5vbWl0ICU+JSBncm91cF9ieShBU1ZOYW1lKSAlPiUgc3VtbWFyaXNlKExvd2lzdCA9IGxhc3QodmFsdWUpKQoKdGhlVGF4YSA8LSB0aGVUYXhhICU+JSBsZWZ0X2pvaW4obXlMb3dpc3QsIGJ5ID0gIkFTVk5hbWUiKQp0aGVUYXhhIDwtIHRoZVRheGEgJT4lIG11dGF0ZShKTmFtZSA9IHN0cl9jKExvd2lzdCwgQVNWTnVtLCBzZXAgPSAiXyIpKQp0aGVUYXhhIDwtIHRoZVRheGEgJT4lIGNvbHVtbl90b19yb3duYW1lcygiQVNWTmFtZSIpCmhlYWQodGhlVGF4YSkKCgp0dDIgPC0gdGF4X3RhYmxlKHRoZVRheGEpCnJvd25hbWVzKHR0MikgPC0gcm93bmFtZXModGhlVGF4YSkKY29sbmFtZXModHQyKSA8LSBjb2xuYW1lcyh0aGVUYXhhKQpwc19yZXRheCA8LSBwcwp0YXhfdGFibGUocHNfcmV0YXgpIDwtIHR0MgpgYGAKClJlbW92ZSBibGFua3MKYGBge3J9CnBzX25vYmxhbmsgPC0gc3Vic2V0X3NhbXBsZXMocHNfcmV0YXgsIFN0cmFpbiAhPSAiQmxhbmsiKQpwc19wbHVzb25lIDwtIHRyYW5zZm9ybV9zYW1wbGVfY291bnRzKHBzX25vYmxhbmssIGZ1bmN0aW9uKHgpIHggKyAxKQpgYGAKCkNvbnZlcnQgdG8gcmVsYXRpdmUgYWJ1bmRhbmNlCmBgYHtyfQpwc3JhIDwtIHBzICU+JSB0cmFuc2Zvcm1fc2FtcGxlX2NvdW50cyggZnVuY3Rpb24oeCkgeC9zdW0oeCkpCmBgYAoKTm9ybWFsaXplIG1pY3JvaWJhbCBjb3VudHMgdG8gb3lzdGVyIGNvdW50cwpgYGB7cn0KcHNfb3lzdGVyIDwtIHBzICU+JSBzdWJzZXRfdGF4YShPcmRlciA9PSAiT3N0cmVvaWRhIikKcHNfbm90X295c3RlciA8LSBwcyAlPiUgc3Vic2V0X3RheGEoT3JkZXIgIT0gIk9zdHJlb2lkYSIpCm95c3Rlcl9zdW1zIDwtIG90dV90YWJsZShwc19veXN0ZXIpQC5EYXRhICU+JSBhcHBseShNQVJHSU4gPSAyLCBzdW0pCm5vdF9veXN0ZXJfY291bnRzIDwtIG90dV90YWJsZShwc19ub3Rfb3lzdGVyKUAuRGF0YQoKb3Zlcl9veXN0ZXIgPC0gc3dlZXAobm90X295c3Rlcl9jb3VudHMsIDIsIG95c3Rlcl9zdW1zLCAiLyIpCm92ZXJfb3lzdGVyX2xvZzEwIDwtIGxvZzEwKG92ZXJfb3lzdGVyKQpgYGAKClRoaXMgaXMgYSB2YXJpYW5jZSBub3JtYWxpemluZyB0cmFuZm9ybWFpdG9uLiBJdHMgYXBwYXJlbnRseSBhbiBhbHRlcm5hdGl2ZSB0byByYXJpZnlpbmcgdGhlIGRhdGEuIApgYGB7cn0KZGVzZXFfcHJlIDwtIHBoeWxvc2VxX3RvX2Rlc2VxMihwcywgZGVzaWduID0gfiBQcm9qZWN0KQojIGRlc2VxX2NvdW50cyA8LSBlc3RpbWF0ZVNpemVGYWN0b3JzKGRlc2VxX3ByZSwgdHlwZSA9ICJwb3Njb3VudHMiKQojIGRlc2VxX2NvdW50c192c3QgPC0gdmFyaWFuY2VTdGFiaWxpemluZ1RyYW5zZm9ybWF0aW9uKGRlc2VxX2NvdW50cykKIyB2c3RfdHJhbnNfY291bnRfdGFiIDwtIGFzc2F5KGRlc2VxX2NvdW50c192c3QpCgojIEVsbGEgb24gU2xhY2sKI2Rlc2VxX3ByZQpkZHMgPSBkZXNlcV9wcmVbcm93U3Vtcyhjb3VudHMoZGVzZXFfcHJlKSkgPiA1LF0KZGRzX2VzZiA8LSBlc3RpbWF0ZVNpemVGYWN0b3JzKGRkcywgdHlwZSA9ICJwb3Njb3VudHMiKQpkZHMwMSA8LSBERVNlcShkZHNfZXNmKQpkZHNfcmVzIDwtIHJlc3VsdHMoZGRzMDEpCgpkZHNfY291bnRzIDwtIGNvdW50cyhkZHMwMSwgbm9ybWFsaXplZCA9IFRSVUUpCgpwc19kZHMgPC0gcHNfcmV0YXgKb3R1X3RhYmxlKHBzX2RkcykgPC0gb3R1X3RhYmxlKGRkc19jb3VudHMsIHRheGFfYXJlX3Jvd3MgPSBUUlVFKQpgYGAKCmV0cyBmaWx0ZXIganVzdCB0aGUgcm93cwoKYGBge3J9CnBzX2NvbW1vbiA8LSBmaWx0ZXJfdGF4YShwc19kZHMsIGZ1bmN0aW9uKHgpIHN1bSh4ID4gMikgPiAoMC4yKmxlbmd0aCh4KSksIFRSVUUpCnBzX2NvbW1vbgpgYGAKCkludGVyZXN0aW5nbHksIHRoaXMgc3RlcCBkb2Vzbid0IHNlZW0gdG8gZWZmZWN0IHRoZSBvcmRpbmF0aW9ucyBtdWNoLgoKYGBge3J9CnBzX295c3RlciA8LSBwc19kZHMgJT4lIHN1YnNldF90YXhhKE9yZGVyID09ICJPc3RyZW9pZGEiKQpwc19ub3Rfb3lzdGVyIDwtIHBzX2NvbW1vbiAlPiUgc3Vic2V0X3RheGEoT3JkZXIgIT0gIk9zdHJlb2lkYSIpICMgb2sgaWYgbm90IGNvbW1vbiwgc28ganVzdCB1c2luZyBkZHMKb3lzdGVyX3N1bXMgPC0gb3R1X3RhYmxlKHBzX295c3RlcilALkRhdGEgJT4lIGFwcGx5KE1BUkdJTiA9IDIsIHN1bSkKbm90X295c3Rlcl9jb3VudHMgPC0gb3R1X3RhYmxlKHBzX25vdF9veXN0ZXIpQC5EYXRhCgpvdmVyX295c3RlciA8LSBzd2VlcChub3Rfb3lzdGVyX2NvdW50cywgMiwgb3lzdGVyX3N1bXMsICIvIikKIyBzbyB0aGlzIHN1ZGRlbnRseSBjb250YWlucyB6ZXJvcywgbWFraW5nIGV2ZXJ5dGhpbmcgZmFpbCwgYnV0IGl0IGRpZG4ndCB1c2VkIHRvLiBXaGF0IGdpdmVzPwpvdmVyX295c3Rlcl9sb2cxMCA8LSBsb2cxMChvdmVyX295c3RlcikKKGRldGVjdGlvbl90aHJlc2ggPC0gbWluKG5hLm9taXQob3Zlcl9veXN0ZXJbb3Zlcl9veXN0ZXIgPiAwXSkpKQpvdmVyX295c3Rlcl9sb2cxMCA8LSBsb2cxMChvdmVyX295c3RlciArIGRldGVjdGlvbl90aHJlc2gpCgpwc19vbyA8LSBwc19ub3Rfb3lzdGVyCm90dV90YWJsZShwc19vbykgPC0gb3R1X3RhYmxlKG92ZXJfb3lzdGVyLCB0YXhhX2FyZV9yb3dzID0gVFJVRSkKcHNfb28gPC0gc3Vic2V0X3NhbXBsZXMocHNfb28sIChTYW1wbGVJRCAlaW4lIGMobmFtZXMob3lzdGVyX3N1bXNbb3lzdGVyX3N1bXMgPiAwXSkpKSkgIyByZW1vdmUgY2FzZXMKCnBzX29vX2xvZyA8LSBwc19ub3Rfb3lzdGVyCm90dV90YWJsZShwc19vb19sb2cpIDwtIG90dV90YWJsZShvdmVyX295c3Rlcl9sb2cxMCwgdGF4YV9hcmVfcm93cyA9IFRSVUUpCgpwc19vb19sb2dfc3MgPC0gc3Vic2V0X3NhbXBsZXMocHNfb29fbG9nLCAhKFByb2plY3QgPT0iTW9jayImIFN0cmFpbiA9PSAiRXZlbiIgJiBSdW4gPT0gIk5vQ3Jhc2giKSkKcHNfb29fc3MgPC0gc3Vic2V0X3NhbXBsZXMocHNfb28sICEoUHJvamVjdCA9PSJNb2NrIiYgU3RyYWluID09ICJFdmVuIiAmIFJ1biA9PSAiTm9DcmFzaCIpKQpgYGAKCiMgTWFrZSB0aGUgb25lIGZpZ3VyZSBmb3IgdGhlIHBhcGVyCgojIyBBIGZ1bmN0aW9uIEkgdXNlCmNvbnZlcnRzIHBoeWxvc2VxIHNhbXBsZSBkYXRhIHRvIGEgZGF0YSBmcmFtZQpgYGB7cn0Kc2FtZF90b19kZiA8LSBmdW5jdGlvbihzYW1kKXsKICBkZiA8LSBzYW1kICU+JSBzYW1wbGVfZGF0YSAlPiUgLkAuRGF0YSAlPiUgbGFwcGx5KGFzLmNoYXJhY3RlcikgJT4lIGRhdGEuZnJhbWUKICBjb2xuYW1lcyhkZikgPC0gc2FtZEBuYW1lcwogIHJvd25hbWVzKGRmKSA8LSBzYW1kQHJvdy5uYW1lcwogIGRmCn0KYGBgCgojIyBBY3R1YWxseSBydW4gdGhlIFJEQQpgYGB7cn0KCnBzX29vX2xvZ19zczEgPC0gcHNfb29fbG9nX3NzICU+JSBzdWJzZXRfc2FtcGxlcygoKFJ1biA9PSJOb0NyYXNoIiAmIFByb2plY3QgPT0gIk5vQ3Jhc2giKSB8IChSdW4gPT0gIkNyYXNoNCIgJiBQcm9qZWN0ID09ICJDcmFzaCIpKSAmIFRyZWF0bWVudCAlaW4lIGMoIkZlZCIsICJTdGFydmUiLCAiUHJlIikpCgp0ZXN0X3JkYSA8LSByZGEodChvdHVfdGFibGUocHNfb29fbG9nX3NzMSkpIH4gUHJvamVjdCArIGFzLmZhY3RvcihTdHJhaW4gPT0gIldpbGQiKSArIGFzLmZhY3RvcihUcmVhdG1lbnQgPT0gIkZlZCIpLCBkYXRhID0gc2FtZF90b19kZihzYW1wbGVfZGF0YShwc19vb19sb2dfc3MxKSkpCnRlc3RfcmRhCnRlc3RfcmRhX2Fub3ZhIDwtIGFub3ZhKHRlc3RfcmRhLCBieSA9ICJtYXJnaW4iLCBwZXJtdXRhdGlvbnMgPSBob3cobnBlcm0gPSA5OTk5OSkpCnRlc3RfcmRhX2Fub3ZhCm15U2NvcmVzIDwtIHNjb3Jlcyh0ZXN0X3JkYSwgY2hvaWNlcyA9IGMoMTo0KSwgc2NhbGluZyA9ICJzeW1tZXRyaWMiKQpgYGAKIyMgU2FtcGxlIERhdGEKYGBge3J9Cm15U2FtcGxlcyA8LSBsZWZ0X2pvaW4oCm15U2NvcmVzJHNpdGVzICU+JSBkYXRhLmZyYW1lICU+JSByb3duYW1lc190b19jb2x1bW4oIlNhbXBsZSIpLApwc19vb19sb2dfc3MxICU+JSBzYW1wbGVfZGF0YSgpICU+JSBzYW1kX3RvX2RmICU+JSByb3duYW1lc190b19jb2x1bW4oIlNhbXBsZSIpLApieSA9ICJTYW1wbGUiCikKYGBgCgojIyBQZXJjZW50IHZhcmlhbmNlIGV4cGxhaW5lZApgYGB7cn0KZWlnc3VtIDwtIHN1bShjKHRlc3RfcmRhJENDQSRlaWcsIHRlc3RfcmRhJENBJGVpZykpCmNjYV9laWcgPC0gdGVzdF9yZGEkQ0NBJGVpZyAvIGVpZ3N1bQpjYV9laWcgPC0gdGVzdF9yZGEkQ0EkZWlnIC8gZWlnc3VtCmFsbF9laWcgPC0gYyhjY2FfZWlnLCBjYV9laWcpCmFsbF9laWdbIlJEQTEiXSAKYGBgCgpgYGB7cn0KcmRhX2VpZ19wY3QgPC0gZGF0YS5mcmFtZShhbGxfZWlnKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJBeCIpICU+JSBkcGx5cjo6cmVuYW1lKEVpZ1BjdCA9ICJhbGxfZWlnIikgJT4lCiAgbXV0YXRlKEF4aXMyID0gb3JkZXJlZChBeCwgbGV2ZWxzID0gQXgpKQpyZGFfZWlnX3BjdApgYGAKCiMjIFNwZWNpZXMKU2VsZWN0IHdoaWNoIHNwZWNpZXMgd2Ugd2lsbCBzaG93LCB0YXJnZXRpbmcgb25lcyB0aGF0IGFyZSBmYXIgZnJvbSAwLCAwLgpgYGB7cn0KbXlTcGVjaWVzIDwtIGxlZnRfam9pbigKICBteVNjb3JlcyRzcGVjaWVzICU+JSBkYXRhLmZyYW1lICU+JSByb3duYW1lc190b19jb2x1bW4oIkFTViIpLApwc19vb19sb2dfc3MxICU+JSB0YXhfdGFibGUoKSAlPiUgLkAuRGF0YSAlPiUgYXMuZGF0YS5mcmFtZSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJBU1YiKQopICU+JSBtdXRhdGUoUkRBRGlzdCA9IHNxcnQoUkRBMV4yICsgUkRBMl4yICsgUkRBMyBeMikpICU+JSAKICBhcnJhbmdlKC1SREFEaXN0KSAlPiUKICBoZWFkKDEwKSAlPiUKICBtdXRhdGUoUmFuayA9IDE6MTApCm15U3BlY2llcwpgYGAKCiMjIENhbGN1bGF0ZSBDZW50cm9pZHMKCmBgYHtyfQpteUNlbnQgPC0gbXlTY29yZXMkY2VudHJvaWRzICU+JSBkYXRhLmZyYW1lICU+JSByb3duYW1lc190b19jb2x1bW4oIlRyZWF0bWVudCIpICU+JQogIHRpZHlyOjpleHRyYWN0KFRyZWF0bWVudCwgYygiVHlwZSIsICJDb25kaXRpb24iKSwgIihbQS1aXVthLXpdKykoW0EtWl0uKikiLCAgcmVtb3ZlID0gRkFMU0UpICU+JQogIG11dGF0ZShUeXBlID0gaWZfZWxzZShzdHJfZGV0ZWN0KFRyZWF0bWVudCwgIldpbGQiKSwgIlN0cmFpbiIsIFR5cGUpKSAlPiUKICBtdXRhdGUoQ29uZGl0aW9uID0gaWZfZWxzZShUeXBlID09ICJTdHJhaW4iICYgc3RyX2RldGVjdChUcmVhdG1lbnQsICJGQUxTRSIpLCAiVGFtZSIsIENvbmRpdGlvbikpICU+JQogIG11dGF0ZShDb25kaXRpb24gPSBpZl9lbHNlKFR5cGUgPT0gIlN0cmFpbiIgJiBzdHJfZGV0ZWN0KFRyZWF0bWVudCwgIlRSVUUiKSwgIldpbGQiLCBDb25kaXRpb24pKSAlPiUKICBtdXRhdGUoVHlwZSA9IGlmX2Vsc2Uoc3RyX2RldGVjdChUcmVhdG1lbnQsICJGZWQiKSwgIkZlZWRpbmciLCBUeXBlKSkgJT4lCiAgbXV0YXRlKENvbmRpdGlvbiA9IGlmX2Vsc2UoVHlwZSA9PSAiRmVlZGluZyIgJiBzdHJfZGV0ZWN0KFRyZWF0bWVudCwgIkZBTFNFIiksICJTdGFydmVkT3JQcmUiLCBDb25kaXRpb24pKSAlPiUKICBtdXRhdGUoQ29uZGl0aW9uID0gaWZfZWxzZShUeXBlID09ICJGZWVkaW5nIiAmIHN0cl9kZXRlY3QoVHJlYXRtZW50LCAiVFJVRSIpLCAiRmVkIiwgQ29uZGl0aW9uKSkKCm15Q2VudDIgPC0gbXlDZW50ICU+JSBmaWx0ZXIoKENvbmRpdGlvbiAlaW4lIGMoIkZlZCIsICJXaWxkIiwgIkNyYXNoIikpKQpgYGAKCiMjIyBNYWluIEZpZ3VyZQpgYGB7cn0KY2NhUGxvdF8xVjJfQSA8LSBteVNhbXBsZXMgJT4lIGdncGxvdChhZXMoeCA9IFJEQTEsIHkgPSBSREEyKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMsIHN0cm9rZSA9IDMsIGFlcyhzaGFwZSA9IFByb2plY3QsIGNvbG9yID0gU3RyYWluID09ICJXaWxkIiwgZmlsbCA9IFRyZWF0bWVudCksIGFscGhhID0gMSkgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKDIxOjI1KSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiZ3JheTQwIiwgImJsYWNrIikpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYyhGZWQgPSAiQmx1ZSIsIFByZSA9ICJEYXJrR3JlZW4iLCBTdGFydmUgPSAiT3JhbmdlIiwgQ3Jhc2ggPSAiUGluayIsIE5vQ3Jhc2ggPSAiV2hpdGUiLCBXaWxkID0gIldoaXRlIikpKwogIGdlb21fcG9pbnQoZGF0YSA9IG15U3BlY2llcywgc2l6ZSA9IDQsIHNoYXBlID0gIisiKSArIAogIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChkYXRhID0gbXlTcGVjaWVzLCBhZXMobGFiZWwgPSBKTmFtZSkgLCBzaXplID0gMykgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2hhcGUgPSAyMSkpLCBjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNoYXBlID0gMjEpKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKY2NhTGVnZW5kIDwtIGdldF9sZWdlbmQoY2NhUGxvdF8xVjJfQSkKCmNjYVBsb3RfMVYyX0IgPC0gY2NhUGxvdF8xVjJfQSArCiAgZ2VvbV9sYWJlbChkYXRhID0gbXlDZW50MiwgYWVzKGxhYmVsID0gQ29uZGl0aW9uLCB4ID0gUkRBMSAqIDEuNzUsIHkgPSBSREEyICogMiwgZmlsbCA9IENvbmRpdGlvbikgLCBzaXplID0gNSkgKwogIGdlb21fc2VnbWVudChkYXRhID0gbXlDZW50MiwgYWVzKHggPSAwLCB5ID0gMCwgeGVuZCA9IFJEQTEgKiAyLjUsIHllbmQgPSBSREEyICogMi41KSwgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuMSwgImluIikpLCBhbHBoYSA9IDAuNSwgc2l6ZSA9IDEpICsKICBjb29yZF9maXhlZChzcXJ0KHRlc3RfcmRhJENDQSRlaWdbMl0vdGVzdF9yZGEkQ0NBJGVpZ1sxXSkpICsKICBsYWJzKHggPSBwYXN0ZTAoIlJEQTEiLCAiICgiLCBzY2FsZXM6OnBlcmNlbnQoYWxsX2VpZ1siUkRBMSJdKSwgIikiKSwKICAgICAgIHkgPSBwYXN0ZTAoIlJEQTIiLCAiICgiLCBzY2FsZXM6OnBlcmNlbnQoYWxsX2VpZ1siUkRBMiJdKSwgIikiKQogICAgICAgKSArCiAgY293cGxvdDo6dGhlbWVfY293cGxvdCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKI3Bsb3RfZ3JpZChjY2FQbG90XzFWMl9CLCBjY2FMZWdlbmQpClByb3RvTmV3RmlnIDwtIHBsb3RfZ3JpZChjY2FQbG90XzFWMl9CLCBjY2FMZWdlbmQsIG5yb3cgPSAyLCByZWxfaGVpZ2h0cyA9IGMoMTAsMSkpCmdnc2F2ZSgiUHJvdG9OZXdGaWcuc3ZnIiwgUHJvdG9OZXdGaWcsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNikKYGBgCgojIyMgVmlldyBNYWluIEZpZ3VyZQpgYGB7cn0KUHJvdG9OZXdGaWcKYGBgCgpJJ20gbm90IHN1cmUgd2h5ICJOb0NyYXNoIiBhbmQgIldpbGQiIHNob3dlZCB1cCBpbiB0aGUgbGVnZW5kLiBJdCBkaWRuJ3QgdXNlZCB0byBkbyB0aGF0LCBidXQgSSdtIG5vdCBnb2luZyB0byBib3RoZXIgdG8gY29ycmVjdCB0aGlzIHJpZ2h0IG5vdy4KCiMgU2VlaW5nIHdoaWNoIHNwZWNpZXMgcmVsYXRlIHRvIGNyYXNoIHZzIG5vbi1jcmFzaApXZSBkb24ndCBzaG93IHRoZXNlIGZpZ3VyZXMgaW4gdGhlIHBhcGVyLCBidXQgd2UgZG8gcmVmZXIgdG8gdGhlbS4KCiMjIEluaXRpYWwgZGF0YSB3cmFuZ2xpbmcKCmBgYHtyfQpwc19vb19sb2dfc3MyIDwtIHBzX29vX2xvZ19zczEKc2FtcGxlX2RhdGEocHNfb29fbG9nX3NzMikgPC0gIG15U2FtcGxlcyAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKCJTYW1wbGUiKSAlPiUgc2FtcGxlX2RhdGEoKQpgYGAKClJlc2hhcGluZyB0byBsb25nIHRha2VzIGEgbGl0dGxlIHdoaWxlLiAofiAyMCBzZWNvbmRzKQpgYGB7cn0KbWVsdF9vb19sb2dfc3MyIDwtIHBzbWVsdChwc19vb19sb2dfc3MyKQpgYGAKCmBgYHtyfQptZWx0Ml9vb19sb2dfc3MyIDwtIG1lbHRfb29fbG9nX3NzMiAlPiUKICBtdXRhdGUobG9nQWJ1bmRhbmNlID0gQWJ1bmRhbmNlKSAlPiUKICBtdXRhdGUoQWJ1bmRhbmNlID0gMTBeKEFidW5kYW5jZSkpCm1lbHQyX29vX2xvZ19zczIgPC0gbWVsdDJfb29fbG9nX3NzMiAlPiUgbGVmdF9qb2luKG15U3BlY2llcyAlPiUgc2VsZWN0KFJEQTEuU3BlYyA9IFJEQTEsIEpOYW1lKSwgYnkgPSAiSk5hbWUiKQptZWx0Ml9vb19sb2dfc3MyICU+JSBoZWFkCmBgYAoKIyMgU3R1ZmYKClJ1biBhbiBsbWUgdG8gc2VlIGlmIGVhY2ggbWljcm9iZSBpcyByZWxhdGVkIHRvIHByb2plY3QsIGhvbGRpbmcgb3V0IHRyZWF0bWVudCBhcyBhIG1peGVkIGVmZmVjdC4KCmBgYHtyfQptb2RmcmFtZSA8LSBtZWx0Ml9vb19sb2dfc3MyICU+JSBzZWxlY3QoUHJvamVjdDpHcm91cCwgbG9nQWJ1bmRhbmNlLCBLaW5nZG9tOkdlbnVzLCBKTmFtZSApICU+JSBncm91cF9ieShKTmFtZSkgJT4lIG5lc3QoZGF0YSA9IFByb2plY3Q6bG9nQWJ1bmRhbmNlKSAlPiUKICAjbXV0YXRlKG1vZCA9IG1hcChkYXRhLCB+dGlkeShsbShkYXRhID0gLiwgbG9nQWJ1bmRhbmNlIH4gUHJvamVjdCkpKSkgJT4lCiAgbXV0YXRlKGxtZSA9IG1hcChkYXRhLCB+dGlkeShsbWVyKGRhdGEgPSAuLCBsb2dBYnVuZGFuY2UgfiBQcm9qZWN0ICsgKDF8VHJlYXRtZW50KSArICgxfFN0cmFpbikpKSkpCmBgYAoKYGBge3J9Cm1vZGZyYW1lMDEgPC0gbW9kZnJhbWUgJT4lIHVubmVzdChsbWUpICU+JSBmaWx0ZXIodGVybSA9PSAiUHJvamVjdE5vQ3Jhc2giKSAlPiUgc2VsZWN0KEtpbmdkb206Sk5hbWUsIGVzdGltYXRlLCBzdGQuZXJyb3IsIHAudmFsdWUpICU+JSBtdXRhdGUoZmRyID0gcC5hZGp1c3QocC52YWx1ZSwgbWV0aG9kID0gIkJIIikpCmBgYAoKYGBge3J9CmdncGxvdGx5KApnZ3Bsb3QobW9kZnJhbWUwMSwgYWVzKHggPSBlc3RpbWF0ZSwgeSA9IGxvZzEwKHAudmFsdWUpLCAgY29sb3IgPSBLaW5nZG9tLCBKTmFtZSA9IEpOYW1lKSkgKyBnZW9tX3BvaW50KCkgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhCYWN0ZXJpYSA9ICJHcmF5MTAiLCBFdWthcnlvdGEgPSAiYmx1ZSIsIEFyY2hhZWEgPSAicmVkIikpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gbG9nMTAoMC4wMSkpKQopCmBgYAoKRXZlcnl0aGluZyBiZWxvdyB0aGUgbGluZSBpcyBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50LiBNb3VzZSBvdmVyIHRoZSBkb3RzIHRvIHNlZSB3aGljaCBiYWN0ZXJpYSBhcmUgd2hpY2guIElmIHBsb3RseSBpcyBnaXZpbmcgeW91IHByb2JsZW1zLCBjb21tZW50IG91dCB0aGUgYGdncGxvdGx5YCBiaXRzIGFuZCB0aGlzIHNob3dzIHVwIGFzIGEgbm9ybWFsIHBsb3QuCgpIb3cgbWFueSBzaWduaWZpY2FudCBhbmQgbm9uIHNpZ25pZmljYW50IEFTVnMgYXJlIHRoZXJlPwoKYGBge3J9Cm1vZGZyYW1lMDEgJT4lIHVuZ3JvdXAgJT4lIHN1bW1hcmlzZShzaWduaWYgPSBzdW0ocC52YWx1ZSA8IDAuMDEpLCB0b3RhbCA9IGxlbmd0aChwLnZhbHVlKSkgJT4lIG11dGF0ZShmcmFjaGl0cyA9IHNpZ25pZi90b3RhbCkgCmBgYAo2MyAlIG9mIHRoZSBhc3ZzIGFyZSByZWxhdGVkIHRvIHRyZWF0bWVudCBwIDwgMC4wMS4=